/*
 * Copyright (C) 2008 by Xume.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.xume.critann;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.xume.critann.callback.AnnotationCallback;
import org.xume.critann.handler.AnnotationHandler;
import org.xume.critann.handler.CriterionAnnotationHandler;
import org.xume.critann.handler.FirstResultAnnotationHandler;
import org.xume.critann.handler.MaxResultsAnnotationHandler;
import org.xume.critann.handler.OrderByAnnotationHandler;
import org.xume.critann.handler.ProjectionAnnotationHandler;

/**
 * $Id: AbstractCriteriaEngine.java 26 2008-10-16 12:45:35Z johan.siebens $
 * 
 * @author Johan Siebens
 */
public abstract class AbstractCriteriaEngine<C, P> implements CriteriaEngine<C, P> {

	private Map<Class<?>, CriteriaBeanInspectionResults> cache = new ConcurrentHashMap<Class<?>, CriteriaBeanInspectionResults>();

	public List<?> list(Object criteriaBean) {
		return parseCriteriaBean(criteriaBean).list();
	}

	public Object uniqueResult(Object criteriaBean) {
		return parseCriteriaBean(criteriaBean).uniqueResult();
	}

	protected abstract CriteriaBuilder<C, P> createCriteriaBuilder(Object criteriaBean, Class<?> entityClass);

	protected List<? extends AnnotationHandler> customAnnotationHandlers() {
		return Collections.emptyList();
	}

	private List<? extends AnnotationHandler> defaultAnnotationHandlers() {
		List<AnnotationHandler> handlers = new ArrayList<AnnotationHandler>();
		handlers.add(new ProjectionAnnotationHandler());
		handlers.add(new CriterionAnnotationHandler());
		handlers.add(new FirstResultAnnotationHandler());
		handlers.add(new MaxResultsAnnotationHandler());
		handlers.add(new OrderByAnnotationHandler());
		return handlers;
	}

	private <T> CriteriaBuilder<C, P> parseCriteriaBean(Object criteriaBean) {
		Class<?> criteriaBeanClass = criteriaBean.getClass();

		CriteriaBeanInspectionResults cbir = cache.get(criteriaBeanClass);
		if (cbir == null) {
			List<AnnotationHandler> handlers = new ArrayList<AnnotationHandler>();
			handlers.addAll(defaultAnnotationHandlers());
			handlers.addAll(customAnnotationHandlers());
			cbir = CriteriaBeanInspectionResults.build(criteriaBeanClass, handlers);
			cache.put(criteriaBeanClass, cbir);
		}

		CriteriaBuilder<C, P> criteriaBuilder = createCriteriaBuilder(criteriaBean, cbir.getEntityClass());

		for (AnnotationCallback<?> callback : cbir.getCallbacks()) {
			callback.doWithCriteriaBuilder(criteriaBuilder, criteriaBean);
		}

		return criteriaBuilder;
	}
}
